﻿// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.

using System;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using Silverlight.Windows.Controls.Primitives;
using System.Collections.Generic;

namespace Silverlight.Windows.Controls
{
    /// <summary>
    /// Represents a pop-up menu that enables a control to expose functionality that is specific to the context of the control.
    /// </summary>
    /// <QualityBand>Preview</QualityBand>
    public class ContextMenuWindow : ContextMenuBase
    {
        static AppWindow _app = new AppWindow();

        public ContextMenuWindow()
            : base()
        {
            DefaultStyleKey = typeof(ContextMenuWindow);
        }

        /// <summary>
        /// Stores a reference to the context menu window.
        /// </summary>
        private static Window _contextWindow;
        private static IntPtr _contextHwnd;
        private double _left;
        private double _top;

        /// <summary>
        /// Updates the location and size of the Popup and overlay.
        /// </summary>
        protected override void UpdateContextMenuPlacement()
        {
            if (Application.Current.IsRunningOutOfBrowser && 
               Application.Current.HasElevatedPermissions)
            {
               if (_contextWindow != null) SizeWindow();
            }
            else if (null != _overlay && null != _rootVisual)
            {
               // Start with the current Popup alignment point
               double x = _popupAlignmentPoint.X;
               double y = _popupAlignmentPoint.Y;
               // Adjust for offset
               x += HorizontalOffset;
               y += VerticalOffset;
               // Measure pop up
               this.Measure(new Size(double.MaxValue, double.MaxValue));
               if( y + this.DesiredSize.Height > _rootVisual.ActualHeight &&
                  y > (_rootVisual.ActualHeight - y))
                  y -= this.DesiredSize.Height;
               if (x + this.DesiredSize.Width > _rootVisual.ActualWidth &&
                  x > (_rootVisual.ActualHeight - x))
                  x -= this.DesiredSize.Width;
               // Set the new location
               Canvas.SetLeft(this, x);
               Canvas.SetTop(this, y);
               // Size the overlay to match the new container
               _overlay.Width = _rootVisual.ActualWidth;
               _overlay.Height = _rootVisual.ActualHeight;
            }
        }

        /// <summary>
        /// Opens the Popup.
        /// </summary>
        /// <param name="position">Position to place the Popup.</param>
        protected override void OpenPopup(Point position)
        {
            ClosePopup();

            _popupAlignmentPoint = position;

            bool first = false;
            if (_contextWindow != null && _contextWindow.Content != null)
            {
                _overlay = _contextWindow.Content as Panel;
                _overlay.MouseLeftButtonDown += HandleOverlayMouseButtonDown;
                _overlay.MouseRightButtonDown += HandleOverlayMouseButtonDown;

                var child = _overlay.Children[0];
                if (child != this)
                {
                    _overlay.Children.Remove(child);
                    _overlay.Children.Add(this);
                }
            }
            else
            {
                first = true;
                _overlay = new Canvas { Background = new SolidColorBrush(Colors.Transparent) };
                _overlay.MouseLeftButtonDown += HandleOverlayMouseButtonDown;
                _overlay.MouseRightButtonDown += HandleOverlayMouseButtonDown;
                _overlay.Children.Add(this);
            }

            if (!(Application.Current.IsRunningOutOfBrowser &&
                  Application.Current.HasElevatedPermissions))
            {
                _popup = new Popup { Child = _overlay };
            }

            SizeChanged += HandleContextMenuOrRootVisualSizeChanged;
            if (null != _rootVisual)
            {
                _rootVisual.SizeChanged += HandleContextMenuOrRootVisualSizeChanged;
            }
            UpdateContextMenuPlacement();
            
            if (ReadLocalValue(DataContextProperty) == DependencyProperty.UnsetValue)
            {
                DependencyObject dataContextSource = Owner ?? _rootVisual;
                SetBinding(DataContextProperty, new Binding("DataContext") { Source = dataContextSource });
            }

            if (!(Application.Current.IsRunningOutOfBrowser &&
                  Application.Current.HasElevatedPermissions))
                _popup.IsOpen = true;
            else
            {             
                int chromeWidth = 0;
                int chromeHeight = 0;
                var style = 
                    _currentWindow == Application.Current.MainWindow
                    ? Deployment.Current.OutOfBrowserSettings.WindowSettings.WindowStyle
                    : _currentWindow.WindowStyle;
                if (style == WindowStyle.SingleBorderWindow)
                {
                    chromeWidth = 10;
                    chromeHeight = 32;
                }

                _top = _currentWindow.Top + position.Y + chromeHeight + VerticalOffset;
                _left = _currentWindow.Left + position.X + chromeWidth + HorizontalOffset;

                if (_contextWindow != null)
                {
                    _contextWindow.Top = _top;
                    _contextWindow.Left = _left;
                    _contextWindow.TopMost = true;
                }
                else
                {
                    var window = new Window { Title = "Context Menu" };
                    var hwnd = Hwnd.FindHwnd(window);
                    _contextHwnd = hwnd;
                    _contextWindow = window;
                    _contextWindow.WindowStyle = WindowStyle.None;
                    _contextWindow.Top = _top;
                    _contextWindow.Left = _left;
                    _contextWindow.Width = 0;
                    _contextWindow.Height = 0;
                    _contextWindow.TopMost = true;
                    _contextWindow.Title = "";
                    _contextWindow.Closing += ContextWindow_Closing;
                    _contextWindow.Show();
                    _contextWindow.Hide();
                    Hwnd.RemoveFromTaskBar(hwnd);
                }

                _contextWindow.Activate();

                _app.AppDeactivated += App_AppActivated;
                _app.WindowDeactivated += App_WindowDeactivated;
                _app.Hook();

                OnLoaded(this, new RoutedEventArgs());
                _overlay.Loaded += OnLoaded;
                
                if (first)
                    _contextWindow.Content = _overlay;

                var content = _currentWindow.Content as UIElement;
                if (content != null)
                {
                    content.LostFocus += ClosePopup;
                    content.GotFocus += ClosePopup;
                }
                this.MouseEnter += ContextMenuWindow_MouseEnter;
            }
            Focus();

            // Update IsOpen
            _settingIsOpen = true;
            IsOpen = true;
            _settingIsOpen = false;

            OnOpened(new RoutedEventArgs());
        }

        static void ContextWindow_Closing(object sender, ClosingEventArgs e)
        {
            _contextWindow.Closing -= ContextWindow_Closing;
            _contextWindow = null;
        }

        void App_AppActivated(object sender, EventArgs e)
        {
            ClosePopup();
        }

        void App_WindowDeactivated(object sender, WindowDeactivatedEventArgs e)
        {
            if (e.Hwnd == _contextHwnd)
                ClosePopup();
        }

        void ContextMenuWindow_MouseEnter(object sender, MouseEventArgs e)
        {
            if(_contextWindow != null)
                _contextWindow.Activate();
        }

        private void OnLoaded(object sender, RoutedEventArgs args)
        {
            SizeWindow();
            _contextWindow.Show();
            Focus();
        }

        private void SizeWindow()
        {           
            this.Measure(new Size(double.MaxValue, double.MaxValue));
            var point = new Point(_contextWindow.Left, _contextWindow.Top);
            var monitor = DisplayMonitors.GetMonitorInfoFromPoint(point);
            // Position context menu above or below the mouse
            if (_top + this.DesiredSize.Height > monitor.WorkArea.Bottom)
                _contextWindow.Top = _top - this.DesiredSize.Height;
            // Position context menu to the left or right of the mouse
            if (_left + this.DesiredSize.Width > monitor.WorkArea.Right)
                _contextWindow.Left = _left - this.DesiredSize.Width;
            else _contextWindow.Left = _left+1;
            // Set 
            _contextWindow.Width = this.DesiredSize.Width;
            _contextWindow.Height = this.DesiredSize.Height;
            if ((null != _overlay))
            {
               _overlay.Width = _contextWindow.Width;
               _overlay.Height = _contextWindow.Height;
            }
        }

        private void ClosePopup(object sender, RoutedEventArgs args)
        {
            ClosePopup();
        }

        /// <summary>
        /// Closes the Popup.
        /// </summary>
        protected override void ClosePopup()
        {
            if (Application.Current.IsRunningOutOfBrowser && 
                Application.Current.HasElevatedPermissions)
            {
                _app.WindowDeactivated -= App_WindowDeactivated;
                _app.AppDeactivated -= App_AppActivated;
                _app.Unhook();

                var content = _currentWindow.Content as UIElement;
                if (content != null)
                {
                    content.LostFocus -= ClosePopup;
                    content.GotFocus -= ClosePopup;
                }
                this.MouseEnter -= ContextMenuWindow_MouseEnter;
                if (null != _contextWindow)
                {
                    _contextWindow.Hide();
                }
            }
            if (null != _popup)
            {
                _popup.IsOpen = false;
                _popup.Child = null;
                _popup = null;
            }
            if (null != _overlay)
            {
                _overlay.Loaded -= OnLoaded;
                _overlay.MouseLeftButtonDown -= HandleOverlayMouseButtonDown;
                _overlay.MouseRightButtonDown -= HandleOverlayMouseButtonDown;
                _overlay = null;
            }
            SizeChanged -= HandleContextMenuOrRootVisualSizeChanged;
            if (null != _rootVisual)
            {
                _rootVisual.SizeChanged -= HandleContextMenuOrRootVisualSizeChanged;
            }       

            // Update IsOpen
            _settingIsOpen = true;
            IsOpen = false;
            _settingIsOpen = false;

            OnClosed(new RoutedEventArgs());
        }

        protected override object GetFocusedElement()
        {
            return
                Application.Current.IsRunningOutOfBrowser &&
                Application.Current.HasElevatedPermissions
                ? FocusManager.GetFocusedElement(_contextWindow)
                : FocusManager.GetFocusedElement();
        }
    }
}